#include <stdio.h>
#include <stdlib.h>

#include "common.h"


using namespace Scenic::Renderer;


//
// class ResourcePool
//

ResourcePool::ResourcePool()
{
}

Resource *ResourcePool::get(int id)
{
	id--;
	if(id >= 0 && id < pool.size)
		return pool.data[id];
	return 0;
}

void ResourcePool::add(Resource *resource)
{
	int i;

	for(i = 0; i < pool.size; i++)
		if(!pool.data[i])
			goto found;

	pool.resize(pool.size + 1);
	i = pool.size - 1;
found:
	resource->id = i + 1;
	pool.data[i] = resource;
}

void ResourcePool::remove(Resource *resource)
{
	for(int i = 0; i < pool.size; i++)
	{
		if(pool.data[i] == resource)
		{
			pool.data[i] = 0;
			break;
		}
	}
}

//
// class JNIEnvPtr
//

JNIEnvPtr::JNIEnvPtr(JavaVM *vm)
{
	this->vm = vm;
	if(vm->GetEnv((void**)&env, JNI_VERSION_1_4) == JNI_EDETACHED)
	{
		vm->AttachCurrentThread((void**)&env, NULL);
		detach = true;
	}
	else
	{
		detach = false;
	}
}

JNIEnvPtr::~JNIEnvPtr()
{
	if(detach)
	{
		vm->DetachCurrentThread();
	}
}

//
// class Resource
//

Resource::Resource(ResourcePool *pool)
{
	javaObject = 0;
	javaObjectRefCount = 0;
	javaVm = 0;

	this->pool = pool;
	pool->add(this);
}

Resource::~Resource()
{
	pool->remove(this);
	if(javaObjectRefCount > 0)
	{
		fprintf(stderr, "Resource::~Resource - javaObjectRefCount > 0\n");
		while(javaObjectRefCount > 0)
			releaseReference();
	}
}

void Resource::addReference(JNIEnv *env, jobject object)
{
	JavaVM *vm;

	env->GetJavaVM(&vm);
	addReference(vm, object);
}

void Resource::addReference(JavaVM *vm, jobject object)
{
	if(javaObjectRefCount == 0)
	{
		javaVm = vm;

		JNIEnvPtr env(javaVm);

		javaObject = env->NewGlobalRef(object);
		javaObjectRefCount++;
	}
}

void Resource::releaseReference()
{
	javaObjectRefCount--;
	if(javaObjectRefCount == 0)
	{
		JNIEnvPtr env(javaVm);
		jobject obj = javaObject;

		javaObject = NULL;			// For thread safety
		env->DeleteGlobalRef(obj);
	}
}

//
// class TessVertexBuffer
//

TessVertexBuffer::TessVertexBuffer()
{
	tessBlocks = 0;
	numTessVertex = 0;
}

TessVertexBuffer::~TessVertexBuffer()
{
	free();
}

TessVertex *TessVertexBuffer::newVertex()
{
	if(tessBlocks == NULL || numTessVertex >= TESS_BLOCK_SIZE)
	{
		TessBlock *b = (TessBlock*)malloc(sizeof(TessBlock));
		
		b->next = tessBlocks;
		tessBlocks = b;
		numTessVertex = 0;
	}
	return &tessBlocks->vertices[numTessVertex++];
}

void TessVertexBuffer::free()
{
	while(tessBlocks) {
		struct TessBlock *next = tessBlocks->next;

		::free(tessBlocks);
		tessBlocks = next;
	}
}

//
// class Color
//

int RGBAFloat::getRGBA32() const
{
#ifdef DIRECTX
	return ((int)(red * 255) << 16) |
		((int)(green * 255) << 8) |
		((int)(blue * 255) << 0) | 
		((int)(alpha * 255) << 24);
#endif
#ifdef OPT_OPENGL
	return ((int)(red * 255) << 0) |
		((int)(green * 255) << 8) |
		((int)(blue * 255) << 16) | 
		((int)(alpha * 255) << 24);
#endif
}

//
// class ClipArea
//

int ClipArea::getModulation()
{
	if(type == SURFACE_TYPE_ALPHA2X)
		return 2;
	if(type == SURFACE_TYPE_ALPHA4X)
		return 4;
	return 1;
}

//
// Affine2
//

Affine2 Affine2::inverse()
{
	Affine2 b;
	double det = m00 * m11 - m01* m10;
	
	b.m00 = m11 / det;
	b.m01 = -m01 / det;
	b.dx = -(b.m00 * dx + b.m01 * dy);
	
	b.m10 = -m10 / det;
	b.m11 = m00 / det;
	b.dy = -(b.m10 * dx + b.m11 * dy);
	
	return b;
}

//
// Primitives
//

Primitives::Primitives(Context *context)
{
	this->context = context;
	vertexStore = 0;
}

Primitives::~Primitives()
{
	if(vertexStore)
		delete vertexStore;
}
